/******************************************************************************
 * This file is part of ZEMB.
 *
 * ZEMB is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * ZEMB is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ZEMB.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Project: zemb
 * Author : FergusZeng
 * Email  : cblock@126.com
 * git	  : https://gitee.com/newgolo/embedme.git
 * Copyright 2014-2020 @ ShenZhen ,China
*******************************************************************************/
#include <stdlib.h>
#include <unistd.h>
#include <sys/timerfd.h>
#include <sys/eventfd.h>
#include "zemb/Poller.h"
#include "zemb/DateTime.h"
#include "zemb/Tracer.h"
/****************************************************************************** 
 * EPOLL事件标志:
 * EPOLLIN : 输入事件,有数据待读取
 * EPOLLOUT: 输出事件,有数据要写入
 * EPOLLET : 边沿触发(事件就绪时,假设对事件没做处理,内核不会反复通知事件就绪)
 *           默认为水平触发(事件就绪时,假设对事件没做处理,内核会反复通知事件就绪)
 *****************************************************************************/
namespace zemb{
Poller::Poller()
{
}

Poller::~Poller()
{
    close();
}

bool Poller::open(int maxEvents, bool onceNotify)
{
    if (m_epfd>0)
    {
        return true;
    }
    m_epfd = epoll_create1(0);
    if (m_epfd<0)
    {
        return false;
    }
    m_events = (struct epoll_event*)calloc(maxEvents,sizeof(struct epoll_event));
    if (m_events==NULL)
    {
        return false;
    }
    m_size = maxEvents;
    m_onceNotify = onceNotify;
    return true;
}
void Poller::close()
{
    if (m_epfd>0)
    {
        ::close(m_epfd);
        m_epfd = -1;
    }
    if (m_events!=NULL)
    {
        free(m_events);
        m_events=NULL;
    }
}
bool Poller::addEvent(PollEvent& pe)
{
    std::lock_guard<std::mutex> lock(m_mutex);
    if (m_epfd<0 || m_eventNum>=m_size || pe.dev()<0)
    {
        return false;
    }
    struct epoll_event evt;
    evt.data.fd = pe.dev();
    switch(pe.event()){
    case PollEvent::POLLIN:
        evt.events = EPOLLIN;
        break;
    case PollEvent::POLLOUT:
        evt.events = EPOLLOUT;
        break;
    default:
        return false;
    }
    if (m_onceNotify)
    {
        evt.events |= EPOLLET;	
    }
    if(epoll_ctl(m_epfd,EPOLL_CTL_ADD,evt.data.fd,&evt)<0)
    {
        return false;
    }
    m_eventNum++;
    return true;
}

bool Poller::removeEvent(PollEvent& pe)
{
    std::lock_guard<std::mutex> lock(m_mutex);
    if (m_epfd<0 || m_eventNum == 0)
    {
        return false;
    }
    struct epoll_event evt;
    evt.data.fd = pe.dev();
    switch(pe.event()){
    case PollEvent::POLLIN:
        evt.events = EPOLLIN;
        break;
    case PollEvent::POLLOUT:
        evt.events = EPOLLOUT;
        break;
    default:
        return false;
    }
    if (m_onceNotify)
    {
        evt.events |= EPOLLET;	
    }
    if(epoll_ctl(m_epfd,EPOLL_CTL_DEL,evt.data.fd,&evt)<0)
    {
        return false;
    }
    m_eventNum--;
    return true;
}

std::vector<std::shared_ptr<PollEvent>> Poller::waitEvent(int usTimeout)
{
    int msTimeout = usTimeout/1000;
    std::vector<std::shared_ptr<PollEvent>> peVect;
    std::lock_guard<std::mutex> lock(m_mutex);
    if (m_epfd<0)
    {
        return peVect;
    }

    int fds = epoll_wait(m_epfd,m_events,m_size,msTimeout);
    if (fds<=0)
    {
        return peVect;
    }

    for(auto i=0; i<fds; i++)
    {
        int fd = m_events[i].data.fd;
        std::shared_ptr<PollEvent> pe=nullptr;
        if(m_events[i].events &EPOLLIN)
        {
            pe = std::make_shared<PollEvent>(fd,PollEvent::POLLIN);
        }
        else if (m_events[i].events &EPOLLOUT)
        {
            pe = std::make_shared<PollEvent>(fd,PollEvent::POLLOUT);
        }
        if (pe!=nullptr)
        {
            peVect.push_back(std::move(pe));
        }
    }
    return peVect;
}

EventPoller::EventPoller()
{
}
EventPoller::~EventPoller()
{
    if (m_rwfd[1]>0)
    {
        ::close(m_rwfd[1]);
    }
    if (m_rwfd[0]>0)
    {
        ::close(m_rwfd[0]);
    }
    if (m_epfd>0)
    {
        ::close(m_epfd);
    }
}

bool EventPoller::setup(int evMask)
{
    struct epoll_event evt;
    if (m_epfd>0)
    {
        return true;
    }
    m_epfd = epoll_create1(0);
    if (m_epfd<0)
    {
        return false;
    }

    if(pipe(m_rwfd)!=0)
    {
        return false;
    }
    evt.data.fd = m_rwfd[0];
    evt.events = EPOLLIN;
    if(epoll_ctl(m_epfd,EPOLL_CTL_ADD,evt.data.fd,&evt)<0)
    {
        return false;
    }
    m_evMask = evMask;
    return true;
}

void EventPoller::clearEvent()
{
    for(;;)
    {
        fd_set readfds;
        FD_ZERO(&readfds);
        FD_SET(m_rwfd[0], &readfds);
        struct timeval tv;
        tv.tv_sec = 0;
        tv.tv_usec = 1000;
        int rc=::select(m_rwfd[0]+1, &readfds, NULL, NULL, &tv);
        if (rc<=0)
        {
            break;
        }
        else if(FD_ISSET(m_rwfd[0], &readfds))
        {
            char buf[256];
            read(m_rwfd[0], buf, sizeof(buf));
        }
    } 
}

void EventPoller::meetEvent(int event)
{
    int ret = write(m_rwfd[1],&event,sizeof(event));
    if (ret!=sizeof(event))
    {
        PRINT_RED("write event error,ret:%d",ret);
    }
}

int EventPoller::waitEvent(int& event,int usTimeout,const std::vector<int>& evWait)
{
    int msTimeout = usTimeout/10000;
    Time startTime = Time::fromMono();
    for(;;)
    {
        int interval = Time::usSinceMono(startTime);
        if(interval>=usTimeout)
        {
            return RC_TIMEOUT;
        }
        int fds = epoll_wait(m_epfd,&m_event,1,msTimeout);
        if(fds>0)
        {
            if ((m_event.data.fd==m_rwfd[0]) && (m_event.events&EPOLLIN))
            {
                int val = 0;
                int ret = read(m_rwfd[0],&val,sizeof(val));
                if(ret==sizeof(val))
                {
                    event = val;
                    event &= m_evMask; 
                    if (evWait.empty())
                    {
                        return RC_OK;
                    }
                    for(auto& ev: evWait)
                    {
                        if (ev==event)
                        {
                            return RC_OK;
                        }
                    }
                }
            }
        }
    }
    return RC_ERROR;
}

PollTimer::PollTimer(const TimerListener& listener,int id):
m_timerID(id)
{
    m_listener = const_cast<TimerListener*>(&listener);
}

PollTimer::~PollTimer()
{
    if (m_tmfd>0)
    {
        close(m_tmfd);
    }
    stop();
}

bool PollTimer::start(int usTimeout,bool repeat)
{
    if (usTimeout<=0)
    {
        return false;
    }

    if (m_tmfd<0)
    {
        m_tmfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC|TFD_NONBLOCK);
        if (m_tmfd<0)
        {
            return false;
        }
        if(!m_poller.open(1,false))
        {
            PRINT("poller open error!");
            return false;
        }
        PollEvent event(m_tmfd,PollEvent::POLLIN);
        if(!m_poller.addEvent(event))
        {
            PRINT("poller add Event error!");
            return false;
        }
    }

    itimerspec ts;
    Time value(usTimeout);//第一次超时时间
    ts.it_value.tv_sec = value.secPart();
    ts.it_value.tv_nsec = value.usPart()*1000;

    if (repeat)
    {
        Time interval(usTimeout);//重复时间间隔
        ts.it_interval.tv_sec = interval.secPart();
        ts.it_interval.tv_nsec = interval.usPart()*1000;
    }
    else
    {
        ts.it_interval.tv_sec = 0;
        ts.it_interval.tv_nsec = 0;
    }
    if(timerfd_settime(m_tmfd,0,&ts,NULL)<0)
    {
        return false;
    }

    m_usInterval = usTimeout;
    return m_thread.start(*this);
}

void PollTimer::stop()
{
    if (m_tmfd>0)
    {
        itimerspec ts;
        ts.it_value.tv_sec = 0;
        ts.it_value.tv_nsec = 0;
        ts.it_interval.tv_sec = 0;
        ts.it_interval.tv_nsec = 0;
        timerfd_settime(m_tmfd,0,&ts,NULL);
    }
    if (m_thread.isRunning())
    {
        m_thread.stop();
    }
}

void PollTimer::run(Thread& thread)
{
    int checkTime = m_usInterval/10;
    while (thread.isRunning()) 
    {
        auto events = m_poller.waitEvent(checkTime);
        if (events.size()>0)
        {
            uint64 timeoutCount = 0;
            int ret = read(m_tmfd,&timeoutCount,sizeof(timeoutCount));
            if (ret==sizeof(timeoutCount) && m_listener!=nullptr)
            {
                if (timeoutCount>1)
                {
                    PRINT_YELLOW("PollTimer::run, timer over run: %llu", timeoutCount);
                }
                m_listener->onTimer(m_timerID);
            }
        }
    }
}

}

